package com.ncf.jucai.mybatis.generator;

import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import lombok.Data;

@Data
public class EmbeddedPlugin extends PluginAdapter {
	@Data
	static public class EmbededContext {
		String tableName;
		String fieldName;
		String fieldClass;
		Map<String, String> subFieldNames  = new HashMap<>();
	}
	private Map<String, List<EmbededContext>> embeddedFields = new HashMap<>();
	@Override
	public void setProperties(Properties properties) {
		super.setProperties(properties);
		Enumeration names = properties.propertyNames();
		while (names.hasMoreElements()) {
			String name = (String) names.nextElement();
			if (name.endsWith("Field")) {
				String field = String.valueOf(properties.getProperty(name));
				String[] segs = field.split(",");
				EmbededContext context = new EmbededContext();
				context.setTableName(segs[0]);
				context.setFieldName(segs[1]);
				context.setFieldClass(segs[2]);
				String[] subFields = segs[3].split("\\|");
				for (String subFieldString: subFields) {
					String[] mapping = subFieldString.split(":");
					context.getSubFieldNames().put(mapping[0], mapping[1]);
				}
				if (!embeddedFields.containsKey(context.getTableName())) {
					embeddedFields.put(context.getTableName(), new ArrayList<EmbededContext>());
				}
				embeddedFields.get(context.getTableName()).add(context);
			}
		}
	}

	@Override
	public boolean validate(List<String> list) {
		return true;
	}

	@Override
	public boolean sqlMapResultMapWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		super.sqlMapResultMapWithoutBLOBsElementGenerated(element, introspectedTable);
		String tableName = introspectedTable.getTableConfiguration().getTableName();
		if (!embeddedFields.containsKey(tableName)) {
			return true;
		}
		List<EmbededContext> contexts = embeddedFields.get(tableName);
		List<Element> elements = element.getElements();
		for (EmbededContext embededContext: contexts) {
			List<XmlElement> embededElements = new ArrayList<>();
			for (Element e: elements) {
				if (e instanceof  XmlElement) {
					XmlElement xe = (XmlElement) e;
					for (Attribute attr :xe.getAttributes()) {
						if (attr.getName().equals("property") && embededContext.getSubFieldNames().containsKey(attr.getValue())) {
							embededElements.add(xe);
							xe.getAttributes().remove(attr);
							xe.addAttribute(new Attribute(attr.getName(), embededContext.getSubFieldNames().get(attr.getValue())));
							break;
						}
					}
				}
			}
			XmlElement columnElement = new XmlElement("association");
			columnElement.addAttribute(new Attribute("property", embededContext.fieldName));
			columnElement.addAttribute(new Attribute("javaType", embededContext.fieldClass));
			for (XmlElement e: embededElements) {
				columnElement.addElement(e);
			}
			element.addElement(columnElement);
			elements.removeAll(embededElements);
		}
		return true;
	}

	@Override
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
		if (!topLevelClass.getAnnotations().contains("@lombok.Data")) {
			topLevelClass.addAnnotation("@lombok.Data");
		}
		topLevelClass.getMethods().clear();
		String tableName = introspectedTable.getTableConfiguration().getTableName();
		if (!embeddedFields.containsKey(tableName)) {
			return true;
		}
		List<EmbededContext> contexts = embeddedFields.get(tableName);
		List<Field> fields = topLevelClass.getFields();
		topLevelClass.getMethods().clear();
		for (EmbededContext embeddedContext: contexts) {
			List<Field> embededFields = new ArrayList<>();
			for (Field field: fields) {
				if (embeddedContext.getSubFieldNames().containsKey(field.getName())) {
					embededFields.add(field);
				}
			}
			Field newField = new Field();
			newField.setType(new FullyQualifiedJavaType(embeddedContext.getFieldClass()));
			newField.setName(embeddedContext.getFieldName());
			topLevelClass.addField(newField);
			topLevelClass.addImportedType(new FullyQualifiedJavaType(embeddedContext.getFieldClass()));
			fields.removeAll(embededFields);
			for (Field field: embededFields) {
				Method m = new Method();
				m.setReturnType(field.getType());
				m.setName(generateGetter(field.getName()));
				m.addBodyLine(String.format("if (%s == null) return null;", newField.getName()));
				m.addBodyLine(String.format("return %s.%s();", newField.getName(), generateGetter(embeddedContext.getSubFieldNames().get(field.getName()))));
				topLevelClass.addMethod(m);
			}
		}
		return true;
	}

	private String generateGetter(String fieldName) {
		return String.format("get%s%s", fieldName.substring(0, 1).toUpperCase(), fieldName.substring(1));
	}
}
